// 引脚定义
/** 屏幕引脚 **/
// #define TFT_MOSI 15
// #define TFT_SCLK 14
// #define TFT_DC   27  // Data Command control pin
// #define TFT_RST  33  // Reset pin (could connect to Arduino RESET pin)

// 光照传感器初始化 IIC
  // Wire.setPins(19,26);
  // 18 SDA 
  // 26 SCL 

// 温湿度引脚
// #define DHTPIN 4
// 舵机引脚
// #define servoPin 5
// 烟雾传感器引脚
// #define MQ2_PIN 36

// 继电器引脚 
// #define JiDianQi_PIN 22

// 火焰报警器
// #define Fire_PIN 23

// 报警灯引脚 
// #define LED_PIN 21

// 蜂鸣器引脚
// #define buzzer 18


#include <stdlib.h>
#include <WiFi.h>
#include <AsyncUDP.h> //引用以使用异步UDP

#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>

#include <Bonezegei_DHT11.h>

#include "ESP32Servo.h"

#include <Wire.h> 

#include <ArduinoJson.h>

#define JiDianQi_PIN 22
#define LED_PIN 21
#define Fire_PIN 23

#define buzzer 18
int freq = 2000;//设置频率
int channel = 0;//通道号，取值0 ~ 15
int resolution = 8;//计数位数，2的8次幂=256

DynamicJsonDocument doc(100);

#define servoPin 5
Servo myservo;

TFT_eSPI tft = TFT_eSPI();  // Invoke library, pins defined in User_Setup.h


int BH1750address = 0x23; 
byte buff[2];

uint8_t web_baojing_flag = 0;
uint8_t web_window_flag = 0;

// 型号定义
#define DHTTYPE DHT11 // DHT 11
#define DHTPIN 4
// DHT11引脚定义及型号选择
// DHT_Unified dht(DHTPIN, DHTTYPE);
Bonezegei_DHT11 dht(DHTPIN);

#define MQ2_PIN 32

// mq2 sensor(MQ2_PIN); /* GPIO36 - ADC1_0  */
// float lpg = 0.0F, co = 0.00F, mq2_smoke = 0.00F;

const char *ssid = "Jiaxuan.dong";
const char *password = "dong2521798182";

AsyncUDP udp;                     //创建UDP对象
unsigned int localUdpPort = 2333; //本地端口号

unsigned int broadcastPort = localUdpPort;
const char *broadcastData = "{\"type\":\"temp\",\"num\":20}";

int temp = 20;
int humi = 5;
int smoke = 0.3;
int light = 800;
float TempThreshold = 30;
float HumiThreshold = 20;
float SmokeThreshold = 1500;
int LightThreshold = 500;
int window_flag = 0;

void Open_led(void);
void Close_led(void);
void Open_Jidianqi(void);
void Close_Jidianqi(void);
void Open_buzzer(void);
void Close_buzzer(void);
void baojing(void);
void jiechubaojing(void);
void turn_light();
void turn_off_light();

void Open_window(void)
{
  turn_light();
}
void Close_window(void)
{
  turn_off_light();
}

void Fire_IRQ(void){
  baojing();
  Serial.printf("Fire_IRQ");
}

void baojing(void)
{
  Open_led();
  Open_Jidianqi();
  Open_buzzer();
  Open_window();
}

void jiechubaojing(void)
{
  Close_led();
  Close_buzzer();
  Close_Jidianqi();
  if(web_window_flag == 0){
    Close_window();
  }
}

void Open_buzzer(void)
{ 
  ledcAttachPin(buzzer,channel);
}

void Close_buzzer(void)
{
  ledcDetachPin(buzzer);
}

void ModePin_set(void){
  pinMode(JiDianQi_PIN,OUTPUT);
  Close_Jidianqi();
  pinMode(LED_PIN,OUTPUT);
  pinMode(Fire_PIN, INPUT|PULLUP );
  attachInterrupt(Fire_PIN, Fire_IRQ, FALLING);
  ledcSetup(channel, freq, resolution);
  
}

void Open_led(void){
  digitalWrite(LED_PIN,HIGH);
}
void Close_led(void){
  digitalWrite(LED_PIN,LOW);
}

void Open_Jidianqi(void){
  digitalWrite(JiDianQi_PIN,LOW);
}
void Close_Jidianqi(void){
  digitalWrite(JiDianQi_PIN,HIGH);
}


int BH1750_Read(int address) //
{
  int i=0;
  Wire.beginTransmission(address);
  Wire.requestFrom(address, 2);
  while(Wire.available()) //
  {
    buff[i] = Wire.read();  // receive one byte
    i++;
  }
  Wire.endTransmission();  
  return i;
}
 
void BH1750_Init(int address) 
{
  Wire.beginTransmission(address);
  Wire.write(0x10);//1lx reolution 120ms
  Wire.endTransmission();
}

int pos = 90; // 定義舵機轉動位置
void turn_mid_light()
{
  myservo.write(90);
}

void turn_light()
{
  myservo.write(0);
}

void turn_off_light()
{
  myservo.write(90);
  // myservo.attach(servoPin, 500, 2500);
  // myservo.write(90 - 45);
  // delay(2000);
  // turn_mid_light();
  // myservo.detach();
}

void onPacketCallBack(AsyncUDPPacket packet)
{
  Serial.print("UDP packet source type: ");
  Serial.println(packet.isBroadcast() ? "broadcast data" : (packet.isMulticast() ? "multicast" : "single"));
  Serial.print("remote addr and port: ");
  Serial.print(packet.remoteIP());
  Serial.print(":");
  Serial.println(packet.remotePort());
  Serial.print("target addr and port: ");
  Serial.print(packet.localIP());
  Serial.print(":");
  Serial.println(packet.localPort());
  Serial.print("data length: ");
  Serial.println(packet.length());
  Serial.print("data: ");
  Serial.write(packet.data(), packet.length());

  deserializeJson(doc,packet.data());
  if(doc["type"] == "temp")
  {
    TempThreshold = doc["num"];
    Serial.printf("change TempThreshold %f",TempThreshold);
    Serial.println();
  }
  if(doc["type"] == "light")
  {
    LightThreshold = doc["num"];
    Serial.printf("change LightThreshold %d",LightThreshold);
    Serial.println();
  }
  if(doc["type"] == "smoke")
  {
    SmokeThreshold = doc["num"];
    Serial.printf("change SmokeThreshold %d",SmokeThreshold);
    Serial.println();
  }

  uint8_t *data = packet.data();
  if(data[2] == '9' && data[3] == '0'){
    web_baojing_flag = 1;
    baojing();
  }
  if(data[2] == '9' && data[3] == '1'){
    web_baojing_flag = 0;
    jiechubaojing();
  }
  if(data[2] == '8' && data[3] == '0'){
    web_window_flag = 1;
    window_flag = 1;
    Open_window();
  }
  if(data[2] == '8' && data[3] == '1'){
    web_window_flag = 0;
    window_flag = 0;
    Close_window();
  }
  Serial.println();

  packet.print("reply data");
  broadcastPort = packet.remotePort();
}

void background_set(void){
  byte red = 31;
  byte green = 0;
  byte blue = 0;
  byte state = 0;
  unsigned int colour = red << 11;
  // Colour changing state machine
    for (int i = 0; i < 130; i++) {
      tft.drawFastVLine(i, 0, 130, colour);
      switch (state) {
        case 0:
          green += 2;
          if (green == 64) {
            green = 63;
            state = 1;
          }
          break;
        case 1:
          red--;
          if (red == 255) {
            red = 0;
            state = 2;
          }
          break;
        case 2:
          blue ++;
          if (blue == 32) {
            blue = 31;
            state = 3;
          }
          break;
        case 3:
          green -= 2;
          if (green == 255) {
            green = 0;
            state = 4;
          }
          break;
        case 4:
          red ++;
          if (red == 32) {
            red = 31;
            state = 5;
          }
          break;
        case 5:
          blue --;
          if (blue == 255) {
            blue = 0;
            state = 0;
          }
          break;
      }
      colour = red << 11 | green << 5 | blue;
    }
}

void screen_driver_init(void){
  tft.init();
  tft.setRotation(0);
}

uint8_t screen_init_flag = 1;
void screen_init(void)
{
  tft.fillScreen(TFT_BLACK);
  background_set();
  tft.setTextColor(TFT_BLACK);
  tft.setCursor (30, 5);
  tft.print("Waiting Hall");
  tft.setCursor (5, 15);
  tft.print("Environmental Test");
  //温度显示
  tft.setCursor (5, 25);
  tft.print("Temp:");
  if(screen_init_flag){
    tft.setCursor (35, 25);
    tft.print(temp);
  }
  tft.setCursor (70, 25);
  tft.print("SheShiDu");
  // 湿度显示
  tft.setCursor (5, 35);
  tft.print("Humi:");
  if(screen_init_flag){
    tft.setCursor (35, 35);
    tft.print(humi);
  }
  tft.setCursor (70, 35);
  tft.print("%");
  // 烟雾浓度显示
  tft.setCursor (5, 45);
  tft.print("Smoke:");
  if(screen_init_flag){
    tft.setCursor (45, 45);
    tft.print(smoke);
  }
  // tft.setCursor (60, 45);
  // tft.print("ppm");
  // 光强显示
  tft.setCursor (5, 55);
  tft.print("Light:");
  if(screen_init_flag){
    tft.setCursor (45, 55);
    tft.print(light);
  }
  tft.setCursor (70, 55);
  tft.print("lux");
  // 温度阈值显示
  tft.setCursor (5, 65);
  tft.print("TempThreshold:");
  // if(screen_init_flag){
    tft.setCursor (90, 65);
    tft.print(TempThreshold);
  // }
  // 湿度阈值显示
  tft.setCursor (5, 75);
  tft.print("HumiThreshold:");
  if(screen_init_flag){
    tft.setCursor (90, 75);
    tft.print(HumiThreshold);
  }
  // 烟雾阈值显示
  tft.setCursor (5, 85);
  tft.print("SmokeThreshold:");
  // if(screen_init_flag){
    tft.setCursor (95, 85);
    tft.print(SmokeThreshold);
  // }
  // 光照阈值显示
  tft.setCursor (5, 95);
  tft.print("LightThreshold:");
  // if(screen_init_flag){
    tft.setCursor (95, 95);
    tft.print(LightThreshold);
  // }
  // 窗户状态显示
  tft.setCursor (5, 105);
  tft.print("Window:");
  if(window_flag == 1){
    tft.setCursor (50, 105);
    tft.print("Open");
  }else{
    tft.setCursor (50, 105);
    tft.print("Close");
  }
  screen_init_flag = 0;
}


void setup()
{
// 调试串口初始化
  Serial.begin(115200);
  Serial.println();
// 继电器、火焰传感器、LED灯、蜂鸣器 引脚初始化
  ModePin_set();

// 屏幕初始化部分
  screen_driver_init();
  screen_init();
// 温湿度初始化
  dht.begin();

// 舵机初始化
  myservo.attach(servoPin);
  // myservo.attach(servoPin, 500, 2500); // 設置舵機控制腳位，並設置最小與最大脈寬
  // int init_angle = myservo.read();     // 讀取舵機初始角度
  // if (init_angle != pos)
  // {
  //   myservo.write(pos);
  //   delay(2000);
  // };
  // myservo.detach();

  // 光照传感器初始化 IIC 初始化
  Wire.setPins(19,26);
  Wire.begin();
  BH1750_Init(BH1750address);

// WIFI及服务初始化和连接
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (!WiFi.isConnected())
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  while (!udp.listen(localUdpPort)) //等待udp监听设置成功
  {
  }
  udp.onPacket(onPacketCallBack); //注册收到数据包事件
}


char humi_val[5];
char temp_val[5];
float tempDeg;
int hum;
uint16_t val=0;
char light_val[4];
uint16_t smoke_num = 0;
char smoke_val[4];

void get_wendu_shidu(void){
  // sensors_event_t event;
  if (dht.getData()) {                         
    tempDeg = dht.getTemperature();      
    float tempFar = dht.getTemperature(true);  
    hum = dht.getHumidity();               
  }
  // sprintf(temp_val, "%f", tempDeg);
  // sprintf(humi_val, "%d", hum);
  
  Serial.print(tempDeg);
  Serial.print("\n");
  Serial.print(hum);
  Serial.print("\n");
  
  // 显示到屏幕
  // screen_init();
  // 温度显示
  tft.setCursor (35, 25);
  tft.print(tempDeg);
  // 湿度显示
  tft.setCursor (35, 35);
  tft.print(hum);
}

void get_smoke(void)
{
  smoke_num = analogRead(MQ2_PIN);
  Serial.print(smoke_num);

  // 烟雾浓度屏幕显示
  tft.setCursor (45, 45);
  tft.print(smoke_num);
}

void get_light(void)
{
  if(2==BH1750_Read(BH1750address))
  {
    val=((buff[0]<<8)|buff[1])/1.2;
    Serial.print(val,DEC);     
    Serial.println("[lx]"); 
  }

  // 显示到屏幕
  sprintf(light_val, "%d", val);
  
  tft.setCursor (45, 55);
  tft.print(light_val);  
}

void Threshold_judge(void)
{
  uint8_t baojing_flag = 0;
  if(tempDeg > TempThreshold || humi > HumiThreshold || val > LightThreshold || smoke_num > SmokeThreshold)
  {
    baojing_flag = 1;
  }
  if(baojing_flag || web_baojing_flag){
    baojing();
  }else{
    jiechubaojing();
  }
}

void udp_broad(void)
{
  static uint8_t count = 0;
  if(count == 0){
    // 广播到UDP服务器
    char udp_data[50] = "{\"type\":\"light\",\"num\":\"";
    strcat(udp_data, light_val);
    strcat(udp_data,"\"}");
    Serial.println(udp_data);
    udp.broadcastTo(udp_data,broadcastPort); //可以使用该方法广播信息
  }
  if(count == 1){
    // 广播到UDP服务器
    char udp_data[50] = "{\"type\":\"temp\",\"num\":\"";
    sprintf(temp_val, "%.2f", tempDeg);
    strcat(udp_data, temp_val);
    strcat(udp_data,"\"}");
    Serial.println(udp_data);
    udp.broadcastTo(udp_data,broadcastPort); //可以使用该方法广播信息
  }
  if(count == 2){
    // 广播到UDP服务器
    char udp_data[50] = "{\"type\":\"humi\",\"num\":\"";
    sprintf(humi_val, "%d", hum);
    strcat(udp_data, humi_val);
    strcat(udp_data,"\"}");
    Serial.println(udp_data);
    udp.broadcastTo(udp_data,broadcastPort); //可以使用该方法广播信息
  }
  if(count == 3){
    // 广播到UDP服务器
    char udp_data[50] = "{\"type\":\"smoke\",\"num\":\"";
    sprintf(smoke_val, "%d", smoke_num);
    strcat(udp_data, smoke_val);
    strcat(udp_data,"\"}");
    Serial.println(udp_data);
    udp.broadcastTo(udp_data,broadcastPort); //可以使用该方法广播信息
  }
  count++;
  if(count > 3){
    count = 0;
  }
}


void loop()
{
  screen_init();
  get_light();
  get_wendu_shidu();
  get_smoke();
  udp_broad();
  Threshold_judge();

  // 烟雾传感器值获取
  

  Serial.print("hello \n");
  delay(2000);
  // udp.broadcastTo(broadcastData, broadcastPort); //可以使用该方法广播信息
}
